﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

/*
 * [StructLayout(LayoutKind.Sequential, Pack = 1)]
        struct IPV6_IP_ADDR
        {
            //字符串，SizeConst为字符串的最大长度
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
            byte[] ip_addr;
        };

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        struct IPV6_ETH_ADDR
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
            byte[] eth_addr;
        };
 */
/*
        /// <summary>
        /// 结构体转byte数组
        /// </summary>
        /// <param name="structObj">要转换的结构体</param>
        /// <returns>转换后的byte数组</returns>
        public static byte[] StructToBytes(object structObj)
        {
            //得到结构体的大小
            int size = Marshal.SizeOf(structObj);
            //创建byte数组
            byte[] bytes = new byte[size];
            //分配结构体大小的内存空间
             IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将结构体拷到分配好的内存空间
             Marshal.StructureToPtr(structObj, structPtr, false);
            //从内存空间拷到byte数组
             Marshal.Copy(structPtr, bytes, 0, size);
            //释放内存空间
             Marshal.FreeHGlobal(structPtr);
            //返回byte数组
            return bytes;
         }
         /// <summary>
        /// byte数组转结构体
        /// </summary>
        /// <param name="bytes">byte数组</param>
        /// <param name="type">结构体类型</param>
        /// <returns>转换后的结构体</returns>
        public static object BytesToStuct(byte[] bytes,Type type)
        {
            //得到结构体的大小
            int size = Marshal.SizeOf(type);
            //byte数组长度小于结构体的大小
            if (size > bytes.Length)
            {
                //返回空
                return null;
             }
            //分配结构体大小的内存空间
             IntPtr structPtr = Marshal.AllocHGlobal(size);
            //将byte数组拷到分配好的内存空间
             Marshal.Copy(bytes,0,structPtr,size);
            //将内存空间转换为目标结构体
            object obj = Marshal.PtrToStructure(structPtr, type);
            //释放内存空间
             Marshal.FreeHGlobal(structPtr);
            //返回结构体
            return obj;
         }
         
         */
namespace cc2530
{
    class IPV6
    {
        byte[] m_Data;
        byte[] m_IpAddr = new byte[16];
        int m_Len;

        const byte IP_V = 0xf0;
        const byte IP_V4 = 0x40;
        const byte IP_V6 = 0x60;

        const byte PROTO_ICMP6 = 58;

        // Ethnet
        const int DESTADDR = 0;
        const int SRCADDR = DESTADDR + 6;
        const int HWTYPE = SRCADDR + 6;
        // IP
        const int VTC = HWTYPE + 2;
        const int TCFLOW = VTC + 1;
        const int FLOW = TCFLOW + 1;
        const int LEN = FLOW + 2;
        const int PROTO = LEN + 2;
        const int TTL = PROTO + 1;
        const int SRCIPADDR = TTL + 1;
        const int DESTIPADDR = SRCIPADDR + 16;

        const int IPV6H_LEN = 40;
        // ICMP6
        const int ICMP6_TYPE = DESTIPADDR + 16;
        const int ICMP6_ICODE = ICMP6_TYPE + 1;
        const int ICMP6_CHKSUM = ICMP6_ICODE + 1;

        const byte ICMP6_RS    = 133;  /**< Router Solicitation */
        const byte ICMP6_RA    = 134;  /**< Router Advertisement */
        const byte ICMP6_NS    = 135;  /**< Neighbor Solicitation */
        const byte ICMP6_NA    = 136;  /**< Neighbor advertisement */
        const byte ICMP6_REDIRECT = 137;  /**< Redirect */

        const byte SLIP_END = 0xc0;
        const byte SLIP_ESC = 0xdb;
        const byte SLIP_ESC_END = 0xdc;
        const byte SLIP_ESC_ESC = 0xdd;
        
        public IPV6(byte[] pBuf, int len, byte[] SelfIpAddr)
        {
            if (SelfIpAddr.Length != 16)
                return;

            if (m_Data == null)
                m_Data = new byte[4096];

            m_Len = len;
            Array.Copy(pBuf, 0, m_Data, VTC, m_Len);
            m_Len += VTC;
            //Console.WriteLine("IPV : {0}", m_Data[VTC]);
            SelfIpAddr.CopyTo(m_IpAddr, 0);
        }

        public IPV6(byte[] pBuf, int len)
        {
            if (m_Data == null)
                m_Data = new byte[4096];

            m_Len = len;
            Array.Copy(pBuf, 0, m_Data, 0, m_Len);
        }
        
        public void IPV6SetEthAddr(byte[] SoureAddr, byte[] DestAddr)
        {
            if (m_Data == null)
                return;


            ///EUI64 标准 xxxxxx10
            Array.Copy(DestAddr, 0, m_Data, DESTADDR, 6);
			SoureAddr[0] |= 0x02;
			SoureAddr[0] &= 0xfe;
            Array.Copy(SoureAddr, 0, m_Data, SRCADDR, 6);

        }

        private UInt16 chksum(UInt16 sum, byte[] data, int len)
        {
	        int dataptr;
	        int last_byte;
            UInt16 t;

	        dataptr = 0;
	        last_byte = len - 1;

	        while (dataptr < last_byte) {   /* At least two more bytes */
		        t = (UInt16)((data[0+dataptr]<<8) + data[1+dataptr]);
		        sum += t;
		        if(sum < t) {
			        sum++;      /* carry */
		        }
		        dataptr += 2;
	        }

	        if(dataptr == last_byte) {
		        t = (UInt16)((data[0+dataptr] << 8) + 0);
		        sum += t;
		        if(sum < t) {
			        sum++;      /* carry */
	            }
            }

	        /* Return sum in host byte order. */
	        return sum;
        }

        private UInt16 upper_layer_chksum(byte[] ip_hdr)
        {
	        UInt16 upper_layer_len;
	        UInt16 sum;

            upper_layer_len = (UInt16)((ip_hdr[LEN]<<8) + ip_hdr[LEN+1]);

	        /* First sum pseudoheader. */
	        /* IP protocol and length fields. This addition cannot carry. */
            sum = (UInt16)(upper_layer_len + ip_hdr[PROTO]);
	        /* Sum IP source and destination addresses. */
            byte[] temp_buf = new byte[2*16];
            Array.Copy(ip_hdr, SRCIPADDR, temp_buf, 0, 2 * 16);
            sum = chksum(sum, temp_buf, 2 * 16);
	        

	        /* Sum TCP header and data. */
            byte[] temp_buf2 = new byte[upper_layer_len];
            Array.Copy(ip_hdr, VTC + IPV6H_LEN, temp_buf2, 0, upper_layer_len);
	        sum = chksum(sum, temp_buf2, upper_layer_len);

            UInt16 sum_FF = 65535;
	        return (sum == 0) ? sum_FF : sum;
        }
        public int IPV6ArpOut()
        {
            int len = 0;
            if (m_Data == null)
                return 0;
           
            if ((m_Data[VTC] & IP_V) == IP_V6) {
                m_Data[HWTYPE] = 0x86;
                m_Data[HWTYPE+1] = 0xdd;

                len = (m_Data[LEN] << 8) + m_Data[LEN + 1];
                
                if (len < m_Len)
                {
                    int i = 0;
                    for (i=0; i<8; ++i)
                    {
                        if (m_Data[DESTIPADDR+i] != m_IpAddr[i])
                            break;;
                    }
                    if (i == 8) {
                        if (m_Data[PROTO] == PROTO_ICMP6)
                        {
                            if (upper_layer_chksum(m_Data) == 0xffff)
                            {
                                if (m_Data[ICMP6_TYPE] == ICMP6_NA)
                                {
                                    len = 24;
                                    m_Data[ICMP6_TYPE + len] = 0x02;
                                    len++;
                                    m_Data[ICMP6_TYPE + len] = 0x01;
                                    len++;


                                    //uip_arp6_update(&ip_hdr_v6->srcipaddr, &pbuf[UIP_IPV6H_LEN + len + 0]);
                                    byte[] mac = new byte[6];
                                    Array.Copy(m_Data, ICMP6_TYPE + len + 2, mac, 0, 6);

                                    m_Data[ICMP6_TYPE + len + 0] = mac[0];// m_Data[ICMP6_TYPE + len + 0];
                                    m_Data[ICMP6_TYPE + len + 1] = mac[1];//m_Data[ICMP6_TYPE + len + 1];
                                    m_Data[ICMP6_TYPE + len + 2] = mac[2];//m_Data[ICMP6_TYPE + len + 2];
                                    m_Data[ICMP6_TYPE + len + 3] = mac[3];//m_Data[ICMP6_TYPE + len + 3];
                                    m_Data[ICMP6_TYPE + len + 4] = mac[4];//m_Data[ICMP6_TYPE + len + 4];
                                    m_Data[ICMP6_TYPE + len + 5] = mac[5];//m_Data[ICMP6_TYPE + len + 5];
                                    //m_Data[ICMP6_TYPE + len + 0] = m_Data[ICMP6_TYPE + len + 2];
                                    //m_Data[ICMP6_TYPE + len + 1] = m_Data[ICMP6_TYPE + len + 3];
                                    //m_Data[ICMP6_TYPE + len + 2] = m_Data[ICMP6_TYPE + len + 4];
                                    //m_Data[ICMP6_TYPE + len + 3] = m_Data[ICMP6_TYPE + len + 5];
                                    //m_Data[ICMP6_TYPE + len + 4] = m_Data[ICMP6_TYPE + len + 6];
                                    //m_Data[ICMP6_TYPE + len + 5] = m_Data[ICMP6_TYPE + len + 7];

									m_Data[ICMP6_TYPE + len + 0] |= 0x02;		// overridden
									m_Data[ICMP6_TYPE + len + 0] &= 0xfe;		// unicast
                                    len += 6;
                                    UInt16 icmpchksum = 0;
                                    m_Data[ICMP6_CHKSUM + 0] = (byte)((icmpchksum & 0xff00) >> 8);
                                    m_Data[ICMP6_CHKSUM + 1] = (byte)(icmpchksum & 0x00ff);

                                    m_Data[LEN + 0] = (byte)((len & 0xff00) >> 8);
                                    m_Data[LEN + 1] = (byte)(len & 0x00ff);

                                    //uip_get_ethhdr6(&ip_hdr_v6->srcipaddr, peth_hdr->ethhdr.src.addr);
                                    m_Len = IPV6H_LEN + len + VTC;
                                    icmpchksum = upper_layer_chksum(m_Data);
                                    icmpchksum = (UInt16)(~icmpchksum);
                                    m_Data[ICMP6_CHKSUM + 0] = (byte)((icmpchksum & 0xff00) >> 8);
                                    m_Data[ICMP6_CHKSUM + 1] = (byte)(icmpchksum & 0x00ff);
                                    return m_Len;
                                }
                            }
                        }
                    }
                    return m_Len; ///你这个不是 tap的 也返回 数据
                }
            }
            return 0;
        }

        public bool IsIpv6()
        {
            return ((m_Data[VTC] & IP_V) == IP_V6);
        }

        public byte[] GetTotalFrame() {
            if (m_Data == null)
                return null;

            byte[] bb = new byte[m_Len];
            Array.Copy(m_Data, 0, bb, 0, m_Len);
            return bb;
        }

        public int GetTotalFrame(byte[] pBuf, int ReadSize)
        {
            if (m_Data == null)
                return 0;

            if (m_Len <= ReadSize)
                ReadSize = m_Len;
            Array.Copy(m_Data, 0, pBuf, 0, ReadSize);
            return ReadSize;
        }
		public int ChangeNeighborSolicitation()
		{
			int len;
			byte[] temp_hw_addr = new byte[8];
			if ((m_Data[VTC] & IP_V) == IP_V6) {
				if ((m_Data[HWTYPE]==0x86) && (m_Data[HWTYPE+1]==0xdd)) {
					len = (m_Data[LEN] << 8) + m_Data[LEN + 1];
                
					if (len < m_Len) {
						if (m_Data[PROTO] == PROTO_ICMP6) {
							if (upper_layer_chksum(m_Data) == 0xffff) {
								if (m_Data[ICMP6_TYPE] == ICMP6_NS) {
									len = 24;
									m_Data[ICMP6_TYPE + len] = 0x01;
									len++;
									m_Data[ICMP6_TYPE + len] = 0x02;
									len++;


                                    //uip_arp6_update(&ip_hdr_v6->srcipaddr, &pbuf[UIP_IPV6H_LEN + len + 0]);
                                    byte[] mac = new byte[6];
                                    Array.Copy(m_Data, ICMP6_TYPE + len + 2, mac, 0, 6);

                                    m_Data[ICMP6_TYPE + len + 0] = mac[0];// m_Data[ICMP6_TYPE + len + 0];
                                    m_Data[ICMP6_TYPE + len + 1] = mac[1];//m_Data[ICMP6_TYPE + len + 1];
                                    m_Data[ICMP6_TYPE + len + 2] = mac[2];//m_Data[ICMP6_TYPE + len + 2];
                                    m_Data[ICMP6_TYPE + len + 3] = mac[3];//m_Data[ICMP6_TYPE + len + 3];
                                    m_Data[ICMP6_TYPE + len + 4] = mac[4];//m_Data[ICMP6_TYPE + len + 4];
                                    m_Data[ICMP6_TYPE + len + 5] = mac[5];//m_Data[ICMP6_TYPE + len + 5];
                                    //m_Data[ICMP6_TYPE + len + 0] = m_Data[ICMP6_TYPE + len + 2];
                                    //m_Data[ICMP6_TYPE + len + 1] = m_Data[ICMP6_TYPE + len + 3];
                                    //m_Data[ICMP6_TYPE + len + 2] = m_Data[ICMP6_TYPE + len + 4];
                                    //m_Data[ICMP6_TYPE + len + 3] = m_Data[ICMP6_TYPE + len + 5];
                                    //m_Data[ICMP6_TYPE + len + 4] = m_Data[ICMP6_TYPE + len + 6];
                                    //m_Data[ICMP6_TYPE + len + 5] = m_Data[ICMP6_TYPE + len + 7];
                                    m_Data[ICMP6_TYPE + len + 0] |= 0x02;		// overridden
                                    m_Data[ICMP6_TYPE + len + 0] &= 0xfe;		// unicast

									
									len += 8;
									UInt16 icmpchksum = 0;
									m_Data[ICMP6_CHKSUM + 0] = (byte)((icmpchksum & 0xff00) >> 8);
									m_Data[ICMP6_CHKSUM + 1] = (byte)(icmpchksum & 0x00ff);

									m_Data[LEN + 0] = (byte)((len & 0xff00) >> 8);
									m_Data[LEN + 1] = (byte)(len & 0x00ff);

									//uip_get_ethhdr6(&ip_hdr_v6->srcipaddr, peth_hdr->ethhdr.src.addr);
									m_Len = IPV6H_LEN + len + VTC;
									icmpchksum = upper_layer_chksum(m_Data);
									icmpchksum = (UInt16)(~icmpchksum);
									m_Data[ICMP6_CHKSUM + 0] = (byte)((icmpchksum & 0xff00) >> 8);
									m_Data[ICMP6_CHKSUM + 1] = (byte)(icmpchksum & 0x00ff);
									return m_Len;
								}
							}
						}
					}
				}
			}
			return m_Len;
		}

        public byte[] GetIpSerialFrame()
        {
            if ((m_Data[VTC] & IP_V) == IP_V6)
            {
                int l = ChangeNeighborSolicitation();
                l -= VTC;
                byte[] d = new byte[l];
                Array.Copy(m_Data, VTC, d, 0, d.Length);
                return d;
            }
            else return null;
        }

        public int GetIpSerialFrame(byte[] pBuf)
        {
            int len;
            if ((m_Data[VTC] & IP_V) == IP_V6)
            {
                len = ChangeNeighborSolicitation();
                len -= VTC;
                int i;
                int out_len = 0;
                for (i = 0; i < len; i++)
                {
                    switch (m_Data[VTC+i])
                    {
                        case SLIP_END:
                            pBuf[out_len] = SLIP_ESC;
                            pBuf[out_len+1] = SLIP_ESC_END;
                            out_len += 2;
                            break;
                        case SLIP_ESC:
                            pBuf[out_len] = SLIP_ESC;
                            pBuf[out_len+1] = SLIP_ESC_ESC;
                            out_len += 2;
                            break;
                        default:
                            pBuf[out_len] = m_Data[VTC + i];
                            out_len++;
                            break;
                    }
                }
                pBuf[out_len] = SLIP_END;
                out_len++;
                return out_len;
            }
            return 0;
        }
    }
}
